package com.fiberhome.opticalbox.net.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import android.util.Log;

public class Base64Code {

	private static final String TAG = Base64Code.class.getSimpleName();

	private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();

	private static final char[] intToBase64 = {
	/* ���� 0 ~ 5 */
	'A', 'B', 'C', 'D', 'E', 'F',
	/* ����6 ~ 18 */
	'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
	/* ���� 19 ~ 31 */
	'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
	/* ���� 32 ~ 44 */
	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
	/* ���� 45 ~ 57 */
	't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
	/* ����58 ~ 63 */
	'6', '7', '8', '9', '+', '/' };

	public static String encode(byte[] data) {
		int start = 0;
		int len = data.length;
		StringBuffer buf = new StringBuffer(data.length * 3 / 2);

		int end = len - 3;
		int i = start;
		int n = 0;

		while (i <= end) {
			int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 0x0ff) << 8) | (((int) data[i + 2]) & 0x0ff);

			buf.append(legalChars[(d >> 18) & 63]);
			buf.append(legalChars[(d >> 12) & 63]);
			buf.append(legalChars[(d >> 6) & 63]);
			buf.append(legalChars[d & 63]);

			i += 3;

			if (n++ >= 14) {
				n = 0;
				buf.append(" ");
			}
		}

		if (i == start + len - 2) {
			int d = ((((int) data[i]) & 0x0ff) << 16) | ((((int) data[i + 1]) & 255) << 8);

			buf.append(legalChars[(d >> 18) & 63]);
			buf.append(legalChars[(d >> 12) & 63]);
			buf.append(legalChars[(d >> 6) & 63]);
			buf.append("=");
		} else if (i == start + len - 1) {
			int d = (((int) data[i]) & 0x0ff) << 16;

			buf.append(legalChars[(d >> 18) & 63]);
			buf.append(legalChars[(d >> 12) & 63]);
			buf.append("==");
		}

		return buf.toString();
	}

	private static int decode(char c) throws Exception {
		if (c >= 'A' && c <= 'Z')
			return ((int) c) - 65;
		else if (c >= 'a' && c <= 'z')
			return ((int) c) - 97 + 26;
		else if (c >= '0' && c <= '9')
			return ((int) c) - 48 + 26 + 26;
		else
			switch (c) {
			case '+':
				return 62;
			case '/':
				return 63;
			case '=':
				return 0;
			default:
				throw new Exception("unexpected code: " + c);
			}
	}

	public static String byteArrayToBase64(byte[] a) {
		int aLen = a.length; // �ܳ���
		int numFullGroups = aLen / 3; // ��3��byte�����4���ַ�Ϊһ�������
		int numBytesInPartialGroup = aLen - 3 * numFullGroups; // ����
		int resultLen = 4 * ((aLen + 2) / 3); // �����������4�������������(aLen+2)/3��֤���������пռ��������=
		StringBuffer result = new StringBuffer(resultLen);

		int inCursor = 0;
		for (int i = 0; i < numFullGroups; i++) {
			int byte0 = a[inCursor++] & 0xff;
			int byte1 = a[inCursor++] & 0xff;
			int byte2 = a[inCursor++] & 0xff;
			result.append(intToBase64[byte0 >> 2]);
			result.append(intToBase64[(byte0 << 4) & 0x3f | (byte1 >> 4)]);
			result.append(intToBase64[(byte1 << 2) & 0x3f | (byte2 >> 6)]);
			result.append(intToBase64[byte2 & 0x3f]);
		}
		// ��������
		if (numBytesInPartialGroup != 0) {
			int byte0 = a[inCursor++] & 0xff;
			result.append(intToBase64[byte0 >> 2]);
			// ����Ϊ1
			if (numBytesInPartialGroup == 1) {
				result.append(intToBase64[(byte0 << 4) & 0x3f]);
				result.append("==");
			} else {
				// ����Ϊ2
				int byte1 = a[inCursor++] & 0xff;
				result.append(intToBase64[(byte0 << 4) & 0x3f | (byte1 >> 4)]);
				result.append(intToBase64[(byte1 << 2) & 0x3f]);
				result.append('=');
			}
		}
		return result.toString();
	}

	public static byte[] decode(String s) {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		try {
			decode(s, bos);
		} catch (IOException e) {
			Log.e(TAG, "decode exception:" + e.getMessage(), e);
		} catch (Exception e) {
			Log.e(TAG, "error characters:" + e.getMessage(), e);
		}

		byte[] decodedBytes = bos.toByteArray();
		try {
			bos.close();
			bos = null;
		} catch (IOException ex) {
			Log.e(TAG, ex.getMessage(), ex);
		}
		return decodedBytes;
	}

	private static void decode(String s, OutputStream os) throws IOException, Exception {
		int i = 0;

		int len = s.length();

		while (true) {
			while (i < len && s.charAt(i) <= ' ')
				i++;

			if (i == len)
				break;

			int tri = (decode(s.charAt(i)) << 18) + (decode(s.charAt(i + 1)) << 12) + (decode(s.charAt(i + 2)) << 6)
					+ (decode(s.charAt(i + 3)));

			os.write((tri >> 16) & 255);
			if (s.charAt(i + 2) == '=')
				break;
			os.write((tri >> 8) & 255);
			if (s.charAt(i + 3) == '=')
				break;
			os.write(tri & 255);

			i += 4;
		}
	}
}
